using System;
using System.Net;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using HslCommunication;
using HslCommunication.BasicFramework;
using HslCommunication.Core;
using HslCommunication.ModBus;
using Catastrophe.AutoSulution.EDC;
using Catastrophe.AutoSulution.TaskM;

namespace HslCommunicationEx.Modubs
{
	public class MbTcpMaster
	{
		public delegate void ExceptionData(ushort id, byte function, byte exception);

		public delegate void ResponseData(ushort id, byte function, byte[] data);

		public enum ServerIniEnum
		{
			SyncSocket,
			AsyncSocket,
			All
		}

		private const byte FctReadCoil = 1;

		private const byte FctReadDiscreteInputs = 2;

		private const byte FctReadHoldingRegister = 3;

		private const byte FctReadInputRegister = 4;

		private const byte FctWriteSingleCoil = 5;

		private const byte FctWriteSingleRegister = 6;

		private const byte FctWriteMultipleCoils = 15;

		private const byte FctWriteMultipleRegister = 16;

		private const byte FctReadWriteMultipleRegister = 23;

		public const byte ExcIllegalFunction = 1;

		public const byte ExcIllegalDataAdr = 2;

		public const byte ExcIllegalDataVal = 3;

		public const byte ExcSlaveDeviceFailure = 4;

		public const byte ExcAck = 5;

		public const byte ExcSlaveIsBusy = 6;

		public const byte ExcGatePathUnavailable = 10;

		public const byte ExcExceptionNotConnected = 253;

		public const byte ExcExceptionConnectionLost = 254;

		public const byte ExcExceptionTimeout = byte.MaxValue;

		private const byte ExcExceptionOffset = 128;

		private const byte ExcSendFailt = 100;

		private static ushort _refresh = 10;

		private readonly byte[] _tcpAsyClBuffer = new byte[2048];

		private readonly byte[] _tcpSynClBuffer = new byte[2048];

		private readonly SimpleHybirdLock _simpleLock;

		private Socket _tcpAsyCl;

		private Socket _tcpSynCl;

		private ServerIniEnum? _serverIniEnum = null;

		public Socket TcpAsyCl => this._tcpAsyCl;

		public Socket TcpSynCl => this._tcpSynCl;

		public string IpAddress { get; set; }

		public int Port { get; set; } = 502;


		public byte Station { get; set; }

		public int ConnectTimeOut { get; set; } = 3000;


		public int RequestTimeout { get; set; } = 2000;


		public ushort Refresh
		{
			get
			{
				return MbTcpMaster._refresh;
			}
			set
			{
				MbTcpMaster._refresh = value;
			}
		}

		public bool IsConnected => this._tcpSynCl?.Connected ?? false;

		public event ResponseData OnResponseData;

		public event ExceptionData OnException;

		public MbTcpMaster()
		{
			this._simpleLock = new SimpleHybirdLock();
		}

		public OperateResult ConnectServer(ServerIniEnum serverIniEnum = ServerIniEnum.SyncSocket, int waitTime = 500)
		{
			try
			{
				this._serverIniEnum = serverIniEnum;
				if (!IPAddress.TryParse(this.IpAddress, out var ip))
				{
					return new OperateResult
					{
						IsSuccess = false,
						Message = "IP地址格式错误"
					};
				}
				Tuple<Task, Exception> tuple = TaskManager.StartTo(delegate
				{
					if (serverIniEnum == ServerIniEnum.AsyncSocket || serverIniEnum == ServerIniEnum.All)
					{
						this._tcpAsyCl = new Socket(ip.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
						this._tcpAsyCl.Connect(new IPEndPoint(ip, this.Port));
						this._tcpAsyCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, this.ConnectTimeOut);
						this._tcpAsyCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, this.ConnectTimeOut);
						this._tcpAsyCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Debug, 1);
					}
					if (serverIniEnum == ServerIniEnum.All)
					{
						Thread.Sleep(waitTime);
					}
					if (serverIniEnum == ServerIniEnum.SyncSocket || serverIniEnum == ServerIniEnum.All)
					{
						this._tcpSynCl = new Socket(ip.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
						this._tcpSynCl.Connect(new IPEndPoint(ip, this.Port));
						this._tcpSynCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, this.ConnectTimeOut);
						this._tcpSynCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, this.ConnectTimeOut);
						this._tcpSynCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Debug, 1);
					}
				}, this.ConnectTimeOut);
				if (serverIniEnum == ServerIniEnum.SyncSocket)
				{
					return new OperateResult
					{
						IsSuccess = (tuple.Item2 == null && tuple.Item1.IsCompleted && (this._tcpSynCl?.Connected ?? false)),
						Message = tuple.Item2?.ToString()
					};
				}
				if (serverIniEnum == ServerIniEnum.AsyncSocket)
				{
					return new OperateResult
					{
						IsSuccess = (tuple.Item2 == null && tuple.Item1.IsCompleted && (this._tcpAsyCl?.Connected ?? false)),
						Message = tuple.Item2?.ToString()
					};
				}
				if (serverIniEnum == ServerIniEnum.All)
				{
					return new OperateResult
					{
						IsSuccess = (tuple.Item2 == null && tuple.Item1.IsCompleted && (this._tcpAsyCl?.Connected ?? false) && (this._tcpSynCl?.Connected ?? false)),
						Message = tuple.Item2?.ToString()
					};
				}
				DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(17, 1);
				defaultInterpolatedStringHandler.AppendLiteral("不支持的serverIniEnum");
				defaultInterpolatedStringHandler.AppendFormatted(serverIniEnum);
				throw new Exception(defaultInterpolatedStringHandler.ToStringAndClear());
			}
			catch (Exception ex)
			{
				return new OperateResult
				{
					IsSuccess = false,
					Message = ex.ToString()
				};
			}
		}

		public void Disconnect()
		{
			this.Dispose();
		}

		~MbTcpMaster()
		{
			this.Dispose();
		}

		public void Dispose()
		{
			if (this._tcpAsyCl != null)
			{
				if (this._tcpAsyCl.Connected)
				{
					try
					{
						this._tcpAsyCl.Shutdown(SocketShutdown.Both);
					}
					catch
					{
					}
					this._tcpAsyCl.Close();
				}
				this._tcpAsyCl = null;
			}
			if (this._tcpSynCl == null)
			{
				return;
			}
			if (this._tcpSynCl.Connected)
			{
				try
				{
					this._tcpSynCl.Shutdown(SocketShutdown.Both);
				}
				catch
				{
				}
				this._tcpSynCl.Close();
			}
			this._tcpSynCl = null;
		}

		internal void CallException(ushort id, byte function, byte exception)
		{
			if (this._serverIniEnum.HasValue && (this._serverIniEnum != ServerIniEnum.SyncSocket || this._tcpSynCl != null) && (this._serverIniEnum != ServerIniEnum.AsyncSocket || this._tcpAsyCl != null) && (this._serverIniEnum != ServerIniEnum.All || (this._tcpAsyCl != null && this._tcpSynCl != null)))
			{
				if (exception == 254)
				{
					this._tcpSynCl = null;
					this._tcpAsyCl = null;
				}
				this.OnException?.Invoke(id, function, exception);
			}
		}

		public void ReadCoilsAsync(ushort startAddress, ushort numInputs, ushort id = 0)
		{
			this.WriteAsyncData(this.CreateReadHeader(id, startAddress, numInputs, 1), id);
		}

		public OperateResult<bool[]> ReadCoilsSync(ushort startAddress, ushort numInputs, ushort id = 0)
		{
			OperateResult<byte[]> operateResult = this.WriteSyncData(this.CreateReadHeader(id, startAddress, numInputs, 1), id);
			if (!operateResult.IsSuccess)
			{
				return new OperateResult<bool[]>
				{
					IsSuccess = false,
					Message = operateResult.Message
				};
			}
			bool[] content = SoftBasic.ByteToBoolArray(operateResult.Content, numInputs);
			return new OperateResult<bool[]>
			{
				IsSuccess = true,
				Content = content
			};
		}

		public void ReadDiscreteInputsAsync(ushort startAddress, ushort numInputs, ushort id = 0)
		{
			this.WriteAsyncData(this.CreateReadHeader(id, startAddress, numInputs, 2), id);
		}

		public OperateResult<bool[]> ReadDiscreteInputsSync(ushort startAddress, ushort numInputs, ushort id = 0)
		{
			OperateResult<byte[]> operateResult = this.WriteSyncData(this.CreateReadHeader(id, startAddress, numInputs, 2), id);
			if (!operateResult.IsSuccess)
			{
				return new OperateResult<bool[]>
				{
					IsSuccess = false,
					Message = operateResult.Message
				};
			}
			bool[] content = SoftBasic.ByteToBoolArray(operateResult.Content, numInputs);
			return new OperateResult<bool[]>
			{
				IsSuccess = true,
				Content = content
			};
		}

		public void ReadHoldingRegisterAsync(ushort startAddress, ushort numInputs, ushort id = 0, byte? cusFunc = null)
		{
			byte function = (byte)((!cusFunc.HasValue) ? 3 : cusFunc.Value);
			this.WriteAsyncData(this.CreateReadHeader(id, startAddress, numInputs, function), id);
		}

		public OperateResult<byte[]> ReadHoldingRegisterSync(ushort startAddress, ushort numInputs, ushort id = 0)
		{
			return this.WriteSyncData(this.CreateReadHeader(id, startAddress, numInputs, 3), id);
		}

		public void ReadInputRegisterAsync(ushort startAddress, ushort numInputs, ushort id = 0)
		{
			this.WriteAsyncData(this.CreateReadHeader(id, startAddress, numInputs, 4), id);
		}

		public OperateResult<byte[]> ReadInputRegisterSync(ushort startAddress, ushort numInputs, ushort id = 0)
		{
			return this.WriteSyncData(this.CreateReadHeader(id, startAddress, numInputs, 4), id);
		}

		public void WriteSingleCoilsAsync(ushort startAddress, bool onOff, ushort id = 0)
		{
			byte[] array = this.CreateWriteHeader(id, startAddress, 1, 1, 5);
			if (onOff)
			{
				array[10] = byte.MaxValue;
			}
			else
			{
				array[10] = 0;
			}
			this.WriteAsyncData(array, id);
		}

		public OperateResult<byte[]> WriteSingleCoilsSync(ushort startAddress, bool onOff, ushort id = 0)
		{
			byte[] array = this.CreateWriteHeader(id, startAddress, 1, 1, 5);
			if (onOff)
			{
				array[10] = byte.MaxValue;
			}
			else
			{
				array[10] = 0;
			}
			return this.WriteSyncData(array, id);
		}

		public void WriteMultipleCoilsAsync(ushort id, ushort startAddress, ushort numBits, byte[] values)
		{
			byte b = Convert.ToByte(values.Length);
			byte[] array = this.CreateWriteHeader(id, startAddress, numBits, (byte)(b + 2), 15);
			Array.Copy(values, 0, array, 13, b);
			this.WriteAsyncData(array, id);
		}

		public OperateResult<byte[]> WriteMultipleCoilsSync(ushort startAddress, bool[] values, ushort id = 0)
		{
			ushort numData = (ushort)values.Length;
			byte[] array = EnDeCode.BoolArrayToByte(values);
			byte b = Convert.ToByte(array.Length);
			byte[] array2 = this.CreateWriteHeader(id, startAddress, numData, (byte)(b + 2), 15);
			Array.Copy(array, 0, array2, 13, b);
			return this.WriteSyncData(array2, id);
		}

		public void WriteSingleRegisterAsync(ushort id, ushort startAddress, byte[] values)
		{
			byte[] array = this.CreateWriteHeader(id, startAddress, 1, 1, 6);
			array[10] = values[0];
			array[11] = values[1];
			this.WriteAsyncData(array, id);
		}

		public OperateResult<byte[]> WriteSingleRegisterSync(ushort id, ushort startAddress, byte[] values)
		{
			byte[] array = this.CreateWriteHeader(id, startAddress, 1, 1, 6);
			array[10] = values[0];
			array[11] = values[1];
			return this.WriteSyncData(array, id);
		}

		public void WriteMultipleRegisterAsync(ushort id, ushort startAddress, byte[] values)
		{
			ushort num = Convert.ToUInt16(values.Length);
			if ((int)num % 2 > 0)
			{
				num = (ushort)(num + 1);
			}
			byte[] array = this.CreateWriteHeader(id, startAddress, Convert.ToUInt16((int)num / 2), Convert.ToUInt16(num + 2), 16);
			Array.Copy(values, 0, array, 13, values.Length);
			this.WriteAsyncData(array, id);
		}

		public OperateResult<byte[]> WriteMultipleRegisterSync(ushort startAddress, byte[] values, ushort id = 0)
		{
			ushort num = Convert.ToUInt16(values.Length);
			if ((int)num % 2 > 0)
			{
				num = (ushort)(num + 1);
			}
			byte[] array = this.CreateWriteHeader(id, startAddress, Convert.ToUInt16((int)num / 2), Convert.ToUInt16(num + 2), 16);
			Array.Copy(values, 0, array, 13, values.Length);
			return this.WriteSyncData(array, id);
		}

		public void ReadWriteMultipleRegisterAsync(ushort id, ushort startReadAddress, ushort numInputs, ushort startWriteAddress, byte[] values)
		{
			ushort num = Convert.ToUInt16(values.Length);
			if ((int)num % 2 > 0)
			{
				num = (ushort)(num + 1);
			}
			byte[] array = this.CreateReadWriteHeader(id, startReadAddress, numInputs, startWriteAddress, Convert.ToUInt16((int)num / 2));
			Array.Copy(values, 0, array, 17, values.Length);
			this.WriteAsyncData(array, id);
		}

		public OperateResult<byte[]> ReadWriteMultipleRegisterSync(ushort startReadAddress, ushort numInputs, ushort startWriteAddress, byte[] values, ushort id = 0)
		{
			ushort num = Convert.ToUInt16(values.Length);
			if ((int)num % 2 > 0)
			{
				num = (ushort)(num + 1);
			}
			byte[] array = this.CreateReadWriteHeader(id, startReadAddress, numInputs, startWriteAddress, Convert.ToUInt16((int)num / 2));
			Array.Copy(values, 0, array, 17, values.Length);
			return this.WriteSyncData(array, id);
		}

		private byte[] CreateReadHeader(ushort id, ushort startAddress, ushort length, byte function)
		{
			byte[] array = new byte[12];
			byte[] bytes = BitConverter.GetBytes((short)id);
			array[0] = bytes[0];
			array[1] = bytes[1];
			array[5] = 6;
			array[6] = this.Station;
			array[7] = function;
			byte[] bytes2 = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)startAddress));
			array[8] = bytes2[0];
			array[9] = bytes2[1];
			byte[] bytes3 = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)length));
			array[10] = bytes3[0];
			array[11] = bytes3[1];
			return array;
		}

		private byte[] CreateWriteHeader(ushort id, ushort startAddress, ushort numData, ushort numBytes, byte function)
		{
			byte[] array = new byte[numBytes + 11];
			byte[] bytes = BitConverter.GetBytes((short)id);
			array[0] = bytes[0];
			array[1] = bytes[1];
			byte[] bytes2 = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)(5 + numBytes)));
			array[4] = bytes2[0];
			array[5] = bytes2[1];
			array[6] = this.Station;
			array[7] = function;
			byte[] bytes3 = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)startAddress));
			array[8] = bytes3[0];
			array[9] = bytes3[1];
			if (function >= 15)
			{
				byte[] bytes4 = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)numData));
				array[10] = bytes4[0];
				array[11] = bytes4[1];
				array[12] = (byte)(numBytes - 2);
			}
			return array;
		}

		private byte[] CreateReadWriteHeader(ushort id, ushort startReadAddress, ushort numRead, ushort startWriteAddress, ushort numWrite)
		{
			byte[] array = new byte[numWrite * 2 + 17];
			byte[] bytes = BitConverter.GetBytes((short)id);
			array[0] = bytes[0];
			array[1] = bytes[1];
			byte[] bytes2 = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)(11 + numWrite * 2)));
			array[4] = bytes2[0];
			array[5] = bytes2[1];
			array[6] = this.Station;
			array[7] = 23;
			byte[] bytes3 = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)startReadAddress));
			array[8] = bytes3[0];
			array[9] = bytes3[1];
			byte[] bytes4 = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)numRead));
			array[10] = bytes4[0];
			array[11] = bytes4[1];
			byte[] bytes5 = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)startWriteAddress));
			array[12] = bytes5[0];
			array[13] = bytes5[1];
			byte[] bytes6 = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)numWrite));
			array[14] = bytes6[0];
			array[15] = bytes6[1];
			array[16] = (byte)(numWrite * 2);
			return array;
		}

		private void WriteAsyncData(byte[] writeData, ushort id)
		{
			if (this._tcpAsyCl != null && this._tcpAsyCl.Connected)
			{
				try
				{
					this._tcpAsyCl.BeginSend(writeData, 0, writeData.Length, SocketFlags.None, new AsyncCallback(OnSend), null);
					this._tcpAsyCl.BeginReceive(this._tcpAsyClBuffer, 0, this._tcpAsyClBuffer.Length, SocketFlags.None, new AsyncCallback(OnReceive), this._tcpAsyCl);
				}
				catch (SystemException)
				{
					this.CallException(id, writeData[7], 254);
				}
			}
			else
			{
				this.CallException(id, writeData[7], 254);
			}
		}

		private void OnSend(IAsyncResult result)
		{
			if (!result.IsCompleted)
			{
				this.CallException(ushort.MaxValue, byte.MaxValue, 100);
			}
		}

		private void OnReceive(IAsyncResult result)
		{
			if (!result.IsCompleted)
			{
				this.CallException(255, byte.MaxValue, 254);
			}
			ushort id = BitConverter.ToUInt16(this._tcpAsyClBuffer, 0);
			byte b = this._tcpAsyClBuffer[7];
			byte[] array;
			if (b >= 5 && b != 23)
			{
				array = new byte[2];
				Array.Copy(this._tcpAsyClBuffer, 10, array, 0, 2);
			}
			else
			{
				array = new byte[this._tcpAsyClBuffer[8]];
				Array.Copy(this._tcpAsyClBuffer, 9, array, 0, this._tcpAsyClBuffer[8]);
			}
			if (b > 128)
			{
				b = (byte)(b - 128);
				this.CallException(id, b, this._tcpAsyClBuffer[8]);
			}
			else
			{
				this.OnResponseData?.Invoke(id, b, array);
			}
		}

		private OperateResult<byte[]> WriteSyncData(byte[] writeData, ushort id)
		{
			Socket tcpSynCl = this._tcpSynCl;
			if (tcpSynCl == null || !tcpSynCl.Connected)
			{
				this.CallException(id, writeData[7], 254);
				return new OperateResult<byte[]>
				{
					IsSuccess = false,
					Message = "未建立连接"
				};
			}
			try
			{
				this._simpleLock.Enter();
				Tuple<Task<OperateResult<byte[]>>, Exception> tuple = TaskManager.StartTo(delegate
				{
					Socket tcpSynCl2 = this._tcpSynCl;
					if (tcpSynCl2 == null || !tcpSynCl2.Connected)
					{
						return new OperateResult<byte[]>
						{
							Message = "socket connected failed!"
						};
					}
					this._tcpSynCl.Send(writeData, 0, writeData.Length, SocketFlags.None);
					int num = this._tcpSynCl.Receive(this._tcpSynClBuffer, 0, this._tcpSynClBuffer.Length, SocketFlags.None);
					byte b = this._tcpSynClBuffer[7];
					if (num == 0)
					{
						this.CallException(id, writeData[7], 254);
					}
					if (b > 128)
					{
						b = (byte)(b - 128);
						this.CallException(id, b, this._tcpSynClBuffer[8]);
						OperateResult<byte[]> obj = new OperateResult<byte[]>
						{
							IsSuccess = false
						};
						DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(15, 2);
						defaultInterpolatedStringHandler.AppendLiteral("Function:");
						defaultInterpolatedStringHandler.AppendFormatted(b);
						defaultInterpolatedStringHandler.AppendLiteral("Error:");
						defaultInterpolatedStringHandler.AppendFormatted(ModbusInfo.GetDescriptionByErrorCode(this._tcpSynClBuffer[8]));
						obj.Message = defaultInterpolatedStringHandler.ToStringAndClear();
						return obj;
					}
					byte[] array;
					if (b >= 5 && b != 23)
					{
						array = new byte[2];
						Array.Copy(this._tcpSynClBuffer, 10, array, 0, 2);
					}
					else
					{
						array = new byte[this._tcpSynClBuffer[8]];
						Array.Copy(this._tcpSynClBuffer, 9, array, 0, this._tcpSynClBuffer[8]);
					}
					return new OperateResult<byte[]>
					{
						IsSuccess = true,
						Content = array
					};
				}, this.RequestTimeout);
				if (tuple.Item2 != null)
				{
					this.CallException(id, writeData[7], 254);
					return new OperateResult<byte[]>
					{
						IsSuccess = false,
						Message = tuple.Item2.ToString()
					};
				}
				return tuple.Item1.Result;
			}
			catch (Exception ex)
			{
				return new OperateResult<byte[]>
				{
					IsSuccess = false,
					Message = ex.ToString()
				};
			}
			finally
			{
				this._simpleLock.Leave();
			}
		}
	}
}
